using Avalonia.Platform;
using Making.Avalonia.WebView.Core.Abstractions;
using Making.Avalonia.WebView.Core.Models;
using Making.Avalonia.WebView.MacOS.Native;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Making.Avalonia.WebView.MacOS;

public class MacOSWebViewProvider : IWebViewProvider, IDisposable
{
    private readonly ILogger<MacOSWebViewProvider>? _logger;
    private readonly Dictionary<IntPtr, IntPtr> _webViews = new();
    private readonly Dictionary<IntPtr, WebViewConfiguration> _configurations = new();
    private bool _disposed;

    public event EventHandler<WebResourceRequestedEventArgs>? WebResourceRequested;
    public event EventHandler<NavigationEventArgs>? NavigationStarting;
    public event EventHandler<NavigationEventArgs>? NavigationCompleted;

    public MacOSWebViewProvider(ILogger<MacOSWebViewProvider>? logger = null)
    {
        _logger = logger;
    }

    public IPlatformHandle CreateWebView(IPlatformHandle parent, WebViewConfiguration configuration)
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            throw new PlatformNotSupportedException("macOS WebView provider only supports macOS platform");

        _logger?.LogDebug("Creating macOS WKWebView control");

        try
        {
            // Create WKWebView instance
            var webView = WKWebViewInterop.objc_msgSend_IntPtr(WKWebViewInterop.class_WKWebView, WKWebViewInterop.sel_alloc);
            
            // Initialize with frame (we'll set proper frame later)
            var frame = new WKWebViewInterop.CGRect(0, 0, 800, 600);
            webView = WKWebViewInterop.objc_msgSend_IntPtr_IntPtr(webView, WKWebViewInterop.sel_initWithFrame, 
                Marshal.AllocHGlobal(Marshal.SizeOf(frame)));

            // Store configuration and webview
            _configurations[webView] = configuration;
            _webViews[webView] = webView;

            // Set initial zoom factor
            if (Math.Abs(configuration.InitialZoomFactor - 1.0) > 0.001)
            {
                WKWebViewInterop.objc_msgSend_void_double(webView, WKWebViewInterop.sel_setMagnification, 
                    configuration.InitialZoomFactor);
            }

            // Navigate to initial source if set
            if (configuration.InitialSource != null)
            {
                NavigateToUrl(webView, configuration.InitialSource);
            }

            return new PlatformHandle(webView, "NSView");
        }
        catch (Exception ex)
        {
            _logger?.LogError(ex, "Failed to create WKWebView");
            throw;
        }
    }

    public void DestroyWebView(IPlatformHandle webView)
    {
        var handle = webView.Handle;
        
        if (_webViews.ContainsKey(handle))
        {
            _logger?.LogDebug("Destroying macOS WKWebView control");
            
            try
            {
                // Note: In a real implementation, we should properly release Objective-C objects
                // This is a simplified version
                _webViews.Remove(handle);
                _configurations.Remove(handle);
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Error disposing WKWebView control");
            }
        }
    }

    public async Task NavigateAsync(IPlatformHandle webView, Uri uri)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            _logger?.LogDebug("Navigating to {Uri}", uri);
            NavigateToUrl(webView.Handle, uri);
        }
    }

    public async Task<string> ExecuteScriptAsync(IPlatformHandle webView, string script)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            _logger?.LogDebug("Executing script in WKWebView");
            
            try
            {
                var tcs = new TaskCompletionSource<string>();
                
                // Create completion handler
                var completionHandler = new WKWebViewInterop.JavaScriptCompletionHandler((result, error) =>
                {
                    try
                    {
                        if (error == IntPtr.Zero && result != IntPtr.Zero)
                        {
                            var resultString = WKWebViewInterop.ExtractStringFromJSResult(result);
                            tcs.SetResult(resultString ?? string.Empty);
                        }
                        else
                        {
                            var errorMessage = error != IntPtr.Zero ? 
                                WKWebViewInterop.ExtractStringFromJSResult(error) : 
                                "JavaScript execution failed";
                            tcs.SetException(new InvalidOperationException(errorMessage));
                        }
                    }
                    catch (Exception ex)
                    {
                        tcs.SetException(ex);
                    }
                });

                // Create the block and execute JavaScript
                var nsScript = WKWebViewInterop.CreateNSString(script);
                var blockPtr = WKWebViewInterop.CreateJavaScriptCompletionBlock(completionHandler);
                
                WKWebViewInterop.objc_msgSend_void_IntPtr_IntPtr(webView.Handle, 
                    WKWebViewInterop.sel_evaluateJavaScript, nsScript, blockPtr);
                
                // Keep references alive
                GC.KeepAlive(completionHandler);
                
                return await tcs.Task;
            }
            catch (Exception ex)
            {
                _logger?.LogError(ex, "Failed to execute script");
                return string.Empty;
            }
        }
        
        return string.Empty;
    }

    public void SetZoomFactor(IPlatformHandle webView, double zoomFactor)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            try
            {
                var clampedZoom = Math.Max(0.1, Math.Min(4.0, zoomFactor));
                WKWebViewInterop.objc_msgSend_void_double(webView.Handle, 
                    WKWebViewInterop.sel_setMagnification, clampedZoom);
                _logger?.LogDebug("Set zoom factor to {ZoomFactor}", clampedZoom);
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to set zoom factor");
            }
        }
    }

    public double GetZoomFactor(IPlatformHandle webView)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            try
            {
                return WKWebViewInterop.objc_msgSend_double(webView.Handle, 
                    WKWebViewInterop.sel_magnification);
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to get zoom factor");
            }
        }
        
        return 1.0;
    }

    public bool CanGoBack(IPlatformHandle webView)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            try
            {
                return WKWebViewInterop.objc_msgSend_bool(webView.Handle, WKWebViewInterop.sel_canGoBack);
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to check if can go back");
            }
        }
        return false;
    }

    public bool CanGoForward(IPlatformHandle webView)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            try
            {
                return WKWebViewInterop.objc_msgSend_bool(webView.Handle, WKWebViewInterop.sel_canGoForward);
            }
            catch (Exception ex)
            {
                _logger?.LogWarning(ex, "Failed to check if can go forward");
            }
        }
        return false;
    }

    public void GoBack(IPlatformHandle webView)
    {
        if (_webViews.ContainsKey(webView.Handle) && CanGoBack(webView))
        {
            WKWebViewInterop.objc_msgSend_void(webView.Handle, WKWebViewInterop.sel_goBack);
            _logger?.LogDebug("WebView navigated back");
        }
    }

    public void GoForward(IPlatformHandle webView)
    {
        if (_webViews.ContainsKey(webView.Handle) && CanGoForward(webView))
        {
            WKWebViewInterop.objc_msgSend_void(webView.Handle, WKWebViewInterop.sel_goForward);
            _logger?.LogDebug("WebView navigated forward");
        }
    }

    public void Reload(IPlatformHandle webView)
    {
        if (_webViews.ContainsKey(webView.Handle))
        {
            WKWebViewInterop.objc_msgSend_void(webView.Handle, WKWebViewInterop.sel_reload);
            _logger?.LogDebug("WebView reloaded");
        }
    }

    public void AddWebResourceFilter(IPlatformHandle webView, string filter)
    {
        // WKWebView resource filtering would require implementing WKURLSchemeHandler
        // This is a more complex implementation that would need additional native code
        _logger?.LogWarning("Web resource filtering not implemented for macOS WKWebView");
    }

    private void NavigateToUrl(IntPtr webView, Uri uri)
    {
        try
        {
            var nsUrl = WKWebViewInterop.CreateNSURL(uri.ToString());
            var request = WKWebViewInterop.CreateNSURLRequest(nsUrl);
            WKWebViewInterop.objc_msgSend_void_IntPtr(webView, WKWebViewInterop.sel_loadRequest, request);
        }
        catch (Exception ex)
        {
            _logger?.LogError(ex, "Failed to navigate to URL: {Uri}", uri);
        }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            foreach (var kvp in _webViews.ToList())
            {
                DestroyWebView(new PlatformHandle(kvp.Key, "NSView"));
            }
            
            _disposed = true;
        }
    }
}